package com.github.taomus.gitoys.git

import com.github.taomus.gitoys.config.entity.GitConfig
import java.io.File
import javax.servlet.ServletConfig
import javax.servlet.http.HttpServletRequest
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.TransportConfigCallback
import org.eclipse.jgit.errors.RepositoryNotFoundException
import org.eclipse.jgit.http.server.GitServlet
import org.eclipse.jgit.internal.storage.file.FileRepository
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.transport.ReceivePack
import org.eclipse.jgit.transport.RefSpec
import org.eclipse.jgit.transport.ServiceMayNotContinueException
import org.eclipse.jgit.transport.SshTransport
import org.eclipse.jgit.transport.Transport
import org.eclipse.jgit.transport.resolver.ReceivePackFactory
import org.eclipse.jgit.transport.resolver.RepositoryResolver
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException
import org.eclipse.jgit.util.FS
import org.eclipse.jgit.transport.OpenSshConfig
import org.eclipse.jgit.transport.JschConfigSessionFactory
import com.jcraft.jsch.Session
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider

class GServlet extends GitServlet {
	override init(ServletConfig config) {
		receivePackFactory = new ReceivePackFactory<HttpServletRequest> {
			override create(HttpServletRequest req,
				Repository db) throws ServiceNotEnabledException, ServiceNotAuthorizedException {
				return new ReceivePack(db)
			}
		}
		repositoryResolver = new RepositoryResolver<HttpServletRequest>() {
			override open(HttpServletRequest req,
				String name) throws RepositoryNotFoundException, ServiceNotAuthorizedException, ServiceNotEnabledException, ServiceMayNotContinueException {
				println(req.parameterMap.keySet.join(","));
				println("ServletPath: " + req.getServletPath());
				println("name: " + name);
				var gfile = new File("./repository");
				return new FileRepository(gfile.absolutePath + "/" + name);
			}
		}
		super.init(config);
	}
}

class GitToy {
	def static clone(GitConfig config) {
		var remote = config.remoteRepository
		var projectName = remote.substring(remote.lastIndexOf("/") + 1);
		var file = new File("./repository/" + projectName);
		file.mkdirs();
		var cloneCommand = Git.cloneRepository().setURI(remote).setBare(true).setGitDir(file).setCloneAllBranches(true);
		if (config.check) {
			if (config.sshKey) {
				cloneCommand.transportConfigCallback = new TransportConfigCallback() {
					override configure(Transport transport) {
						var sshTransport = transport as SshTransport;
						sshTransport.setSshSessionFactory(
							new JschConfigSessionFactory() {
								override configure(OpenSshConfig.Host host, Session session) {
									session.setConfig("StrictHostKeyChecking", "no");
								}

								override createDefaultJSch(FS fs) {
									var jSch = super.createDefaultJSch(fs);
									jSch.addIdentity(#[config.sshkeypath, "id_rsa"].join("/"));
									// jSch.setKnownHosts(#[config.sshkeypath, "known_hosts"].join("/"));
									return jSch;
								}
							}
						);
					}
				}
			} else {
				cloneCommand.setCredentialsProvider(
					new UsernamePasswordCredentialsProvider(config.username, config.password));
			}
		}
		try (var git = cloneCommand.call()) {
			var refs = git.lsRemote.setHeads(true).setRemote(remote).call();
			// var refs = Git.lsRemoteRepository().setHeads(true).setRemote(remote).call();
			var StringBuffer s = new StringBuffer();
			for (ref : refs) {
				s.append(ref.getName());
				s.append("\n");
			}
			s.append("clone success")
			return s.toString();
		}
	}

	def static syncRemote(GitConfig config) {
		var remote = config.remoteRepository
		var branch = config.branch
		var String projectName = remote.substring(remote.lastIndexOf("/") + 1);
		var File a = new File("./repository/" + projectName)
		var git = Git.open(a)
		var pushCommand = git.push.add(branch).setRemote(remote)
		if (config.check) {
			if (config.sshKey) {
				pushCommand.transportConfigCallback = new TransportConfigCallback() {
					override configure(Transport transport) {
						var sshTransport = transport as SshTransport;
						sshTransport.setSshSessionFactory(
							new JschConfigSessionFactory() {
								override configure(OpenSshConfig.Host host, Session session) {
									session.setConfig("StrictHostKeyChecking", "no");
								}

								override createDefaultJSch(FS fs) {
									var jSch = super.createDefaultJSch(fs);
									jSch.addIdentity(#[config.sshkeypath, "id_rsa"].join("/"));
									// jSch.setKnownHosts(#[config.sshkeypath, "known_hosts"].join("/"));
									return jSch;
								}
							}
						);
					}
				}
			} else {
				pushCommand.setCredentialsProvider(
					new UsernamePasswordCredentialsProvider(config.username, config.password));
			}
		}
		var infos = pushCommand.call()
		var buffer = new StringBuffer();
		for (info : infos) {
			buffer.append(info.messages);
			buffer.append("\n")
		}
		return buffer.toString();
	}

	def static update(GitConfig config) {
		var remote = config.remoteRepository
		var String projectName = remote.substring(remote.lastIndexOf("/") + 1);
		var File a = new File("./repository/" + projectName);
		var refs = Git.lsRemoteRepository().setHeads(true).setRemote(remote).call();
		var log = new StringBuffer("fetch:\n");
		for (ref : refs) {
			var String refStr = ref.getName();
			var fetchCommand = Git.open(a).fetch().setRemote(remote).setRefSpecs(#[new RefSpec(refStr + ":" + refStr)])
			if (config.check) {
				if (config.sshKey) {
					fetchCommand.transportConfigCallback = new TransportConfigCallback() {
						override configure(Transport transport) {
							var sshTransport = transport as SshTransport;
							sshTransport.setSshSessionFactory(
								new JschConfigSessionFactory() {
									override configure(OpenSshConfig.Host host, Session session) {
										session.setConfig("StrictHostKeyChecking", "no");
									}

									override createDefaultJSch(FS fs) {
										var jSch = super.createDefaultJSch(fs);
										jSch.addIdentity(#[config.sshkeypath, "id_rsa"].join("/"));
										// jSch.setKnownHosts(#[config.sshkeypath, "known_hosts"].join("/"));
										return jSch;
									}
								}
							);
						}
					}
				} else {
					fetchCommand.setCredentialsProvider(
						new UsernamePasswordCredentialsProvider(config.username, config.password));
				}
			}
			var result = fetchCommand.call();
			log.append(result.getMessages());
			log.append("\n");
		}
		log.append("Fetch success")
		return log.toString();
	}
}

class GitToyServer {
	static String PROJECT_PATH = System.getProperty("user.dir");
	static String WEB_APP_PATH = PROJECT_PATH + File.separatorChar + "WebRoot";
	static String CATALINA_HOME = PROJECT_PATH + "/Embedded/Tomcat";
	Tomcat tomcat = null;

	def start(String ip, int port) {
		tomcat = new Tomcat();
		tomcat.hostname = ip;
		tomcat.setPort(port);
		tomcat.setBaseDir(CATALINA_HOME);
		var file = new File(WEB_APP_PATH);
		if (!file.exists) {
			file.mkdirs
		}
		var contextPath = "/toy"
		var Context context = tomcat.addWebapp(contextPath, WEB_APP_PATH);
		var servlet = tomcat.addServlet(contextPath, "gitservlet", new GServlet());
		context.addServletMappingDecoded("/*", "gitservlet");
		tomcat.getConnector().setURIEncoding("UTF-8");
		servlet.loadOnStartup = 1
		var gfile = new File("./repository");
		if(!gfile.exists){
			gfile.mkdirs
		}
		servlet.addInitParameter("base-path", gfile.absolutePath)
		servlet.addInitParameter("export-all", "true");
		//tomcat.start();
		tomcat.server.start
		tomcat.server.await;
	}

	def stop() {
		tomcat.server.stop
		tomcat.destroy
	}
}
